/*
 * Copyright (C) 2014 Balys Valentukevicius
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.karim;

import io.karim.event.DispatchHelper;
import io.karim.event.impl.ViewGroupHelper;
import io.karim.event.interfaces.ViewGroup;
import io.karim.gesture.GestureDetector;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorGroup;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.colors.RgbColor;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.*;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.hiviewdfx.HiLog;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

public class MaterialRippleLayout extends DirectionalLayout implements Component.TouchEventListener, ViewGroup, Component.DrawTask {
    public static final int DEFAULT_DURATION = 250;
    public static final int DEFAULT_FADE_DURATION = 125;
    public static final int DEFAULT_DIAMETER_DP = 20;
    public static final float DEFAULT_ALPHA = 0.2f;
    public static final int DEFAULT_COLOR = Color.WHITE.getValue();
    public static final boolean DEFAULT_DELAY_CLICK = false;
    public static final boolean DEFAULT_PERSISTENT = false;
    public static final boolean DEFAULT_SEARCH_ADAPTER = false;
    public static final boolean DEFAULT_RIPPLE_OVERLAY = false;
    public static final int DEFAULT_ROUNDED_CORNERS_DP = 0;
    public static final int FADE_EXTRA_DELAY = 50;
    public static final long HOVER_DURATION = 2500;

    private final Paint paint = new Paint();
    private final Rect bounds = new Rect();

    private int rippleColor;
    private int rippleHighlightColor;
    private boolean rippleOverlay;
    private int rippleDiameterPx;
    private int rippleDuration;
    private float rippleAlphaInt;
    private boolean rippleDelayClick;
    private int rippleFadeDuration;
    private boolean ripplePersistent;
    private boolean rippleInAdapter;
    private float rippleRoundedCornersPx;

    private float radius;

    private ListContainer parentAdapter;
    private Component childView;

    private AnimatorGroup rippleAnimator;
    private AnimatorValue hoverAnimator;

    private Point currentCoordinates = new Point();
    private Point previousCoordinates = new Point();
    private boolean eventCancelled;
    private boolean prepressed;
    private int positionInAdapter;

    private final GestureDetector gestureDetector;
    private PressedEvent pendingPressEvent;
    private final ViewGroupHelper impl;
    private final EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());

    public static RippleBuilder on(Component view) {
        return new RippleBuilder(view);
    }

    public MaterialRippleLayout(Context context) {
        this(context, null);
    }

    public MaterialRippleLayout(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    @Override
    public void addComponent(Component childComponent, ComponentContainer.LayoutConfig layoutConfig) {
        if (getChildCount() > 0) {
            throw new IllegalStateException("MaterialRippleLayout can host only one child");
        }
        childView = childComponent;
        super.addComponent(childComponent, layoutConfig);
    }

    @Override
    public void setClickedListener(ClickedListener listener) {
        if (childView == null) {
            throw new IllegalStateException("MaterialRippleLayout must have a child view to handle clicks");
        }
        childView.setClickedListener(listener);
        super.setClickedListener(listener);
    }

    private void cancelPressedEvent() {
        if (pendingPressEvent != null) {
            handler.removeTask(pendingPressEvent);
            prepressed = false;
        }
    }

    private boolean mHasPerformedLongPress;

    private void startHover() {
        if (eventCancelled) {
            return;
        }

        if (hoverAnimator != null) {
            hoverAnimator.cancel();
        }
        HiLog.error(MaterialTabs.label, " onDraw  startHover");
        final float radius = (float) (Math.sqrt(Math.pow(getWidth(), 2) + Math.pow(getHeight(), 2)) * 1.2f);
        hoverAnimator = new AnimatorValue();
        hoverAnimator.setCurveType(Animator.CurveType.ACCELERATE);
        hoverAnimator.setDuration(HOVER_DURATION);
        hoverAnimator.setValueUpdateListener((animatorValue, v) -> setRadius(radius - (1.0f - v) * (radius - rippleDiameterPx)));
        hoverAnimator.start();


    }

    private void startRipple(final Runnable animationEndRunnable) {
        if (eventCancelled) {
            return;
        }

        float endRadius = getEndRadius();

        cancelAnimations();

        rippleAnimator = new AnimatorGroup();
        rippleAnimator.setStateChangedListener(new Animator.StateChangedListener() {
            @Override
            public void onStart(Animator animator) {

            }

            @Override
            public void onStop(Animator animator) {

            }

            @Override
            public void onCancel(Animator animator) {

            }

            @Override
            public void onEnd(Animator animator) {
                if (!ripplePersistent) {
                    setRadius(0);
                    setRippleAlphaInt(rippleAlphaInt);
                }
                if (animationEndRunnable != null && rippleDelayClick) {
                    animationEndRunnable.run();
                }
                childView.setPressState(false);
                ShapeElement shapeElement = new ShapeElement();
                shapeElement.setRgbColor(RgbColor.fromArgbInt(Color.TRANSPARENT.getValue()));
                setBackground(shapeElement);
            }

            @Override
            public void onPause(Animator animator) {

            }

            @Override
            public void onResume(Animator animator) {

            }
        });
        AnimatorValue ripple = new AnimatorValue();
        ripple.setDuration(rippleDuration);
        ripple.setCurveType(Animator.CurveType.DECELERATE);
        ripple.setValueUpdateListener((animatorValue, v) -> setRadius(endRadius - (1.0f - v) * (endRadius - radius)));
        AnimatorValue fade = new AnimatorValue();
        fade.setDuration(rippleDuration);
        fade.setCurveType(Animator.CurveType.ACCELERATE);
        fade.setDelay(rippleDuration - rippleFadeDuration - FADE_EXTRA_DELAY);
        fade.setValueUpdateListener((animatorValue, v) -> setRippleAlphaInt(rippleAlphaInt - v * rippleAlphaInt));
        AnimatorGroup.Builder builder = rippleAnimator.build();
        if (ripplePersistent) {
            builder.addAnimators(ripple);
        } else if (getRadius() > endRadius) {
            fade.setDelay(0);
            builder.addAnimators(fade);
        } else {
            builder.addAnimators(ripple, fade);
        }
        rippleAnimator.start();
    }

    private void cancelAnimations() {
        if (rippleAnimator != null) {
            rippleAnimator.cancel();
            rippleAnimator.clear();
        }

        if (hoverAnimator != null) {
            hoverAnimator.cancel();
        }
    }

    private boolean isInScrollingContainer() {
        ComponentParent p = getComponentParent();
        while (p instanceof ComponentParent) {
            if (p instanceof ListContainer) {
                return true;
            }
            p = p.getComponentParent();
        }
        return false;
    }

    private ListContainer findParentAdapterView() {
        if (parentAdapter != null) {
            return parentAdapter;
        }
        ComponentParent current = getComponentParent();
        while (true) {
            if (current instanceof ListContainer) {
                parentAdapter = (ListContainer) current;
                return parentAdapter;
            } else {
                current = current.getComponentParent();
            }
        }
    }

    private void setPositionInAdapter() {
        if (rippleInAdapter) {
            positionInAdapter = findParentAdapterView().getChildIndex(MaterialRippleLayout.this);
        }
    }

    private boolean adapterPositionChanged() {
        if (rippleInAdapter) {
            int newPosition = findParentAdapterView().getChildIndex(MaterialRippleLayout.this);
            final boolean changed = newPosition != positionInAdapter;
            positionInAdapter = newPosition;
            if (changed) {
                cancelPressedEvent();
                cancelAnimations();
                childView.setPressState(false);
                setRadius(0);
            }
            return changed;
        }
        return false;
    }

    private boolean findClickableViewInChild(Component view, int x, int y) {
        if (view instanceof ComponentContainer) {
            ComponentContainer viewGroup = (ComponentContainer) view;
            for (int i = 0; i < viewGroup.getChildCount(); i++) {
                Component child = viewGroup.getComponentAt(i);
                final Rect rect = new Rect();
                child.setComponentPosition(rect);

                final boolean contains = rect.left <= x && rect.right >= x && rect.top <= y && rect.bottom >= y;
                if (contains) {
                    return findClickableViewInChild(child, x - rect.left, y - rect.top);
                }
            }
        } else if (view != childView) {
            return (view.isEnabled() && (view.isClickable() || view.isLongClickOn() || view.isTouchFocusable()));
        }

        return view.isTouchFocusable();
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        final boolean positionChanged = adapterPositionChanged();
        if (rippleOverlay) {
            if (!positionChanged) {
                if (rippleRoundedCornersPx != 0) {
                    Path clipPath = new Path();
                    RectFloat rect = new RectFloat(0, 0, component.getEstimatedWidth(), component.getEstimatedHeight());
                    clipPath.addRoundRect(rect, rippleRoundedCornersPx, rippleRoundedCornersPx, Path.Direction.CLOCK_WISE);
                    canvas.clipPath(clipPath, Canvas.ClipOp.INTERSECT);
                }
                canvas.drawCircle(currentCoordinates.getPointX(), currentCoordinates.getPointY(), radius, paint);
            }
        } else {
            if (!positionChanged) {
                canvas.drawCircle(currentCoordinates.getPointX(), currentCoordinates.getPointY(), radius, paint);
            }
        }
    }

    public MaterialRippleLayout(Context context, AttrSet attrs, String defStyle) {
        super(context, attrs, defStyle);
        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public void onLongPress(TouchEvent e) {
                mHasPerformedLongPress = childView.executeLongClick();
                if (mHasPerformedLongPress) {
                    startRipple(null);
                    cancelPressedEvent();
                }
            }

            @Override
            public boolean onDown(TouchEvent e) {
                mHasPerformedLongPress = false;
                return super.onDown(e);
            }
        });

        rippleColor = AttrUtils.getColorFromAttr(attrs, "mrlRippleColor", DEFAULT_COLOR);

        // Making default ripple highlight color the same as rippleColor but with 1/4 the alpha.
        RgbColor rgbColor = RgbColor.fromArgbInt(rippleColor);
        rippleHighlightColor = Color.argb((int) (rgbColor.getAlpha() * 0.25), rgbColor.getRed(), rgbColor.getGreen(),
                rgbColor.getBlue());
        rippleHighlightColor = AttrUtils.getColorFromAttr(attrs, "mrlRippleHighlightColor", rippleHighlightColor);
        rippleDiameterPx = AttrHelper.vp2px(AttrUtils.getDimensionFromAttr(attrs, "mrlRippleDiameter",
                DEFAULT_DIAMETER_DP), context);
        rippleOverlay = AttrUtils.getBooleanFromAttr(attrs, "mrlRippleOverlay", DEFAULT_RIPPLE_OVERLAY);
        rippleDuration = AttrUtils.getIntegerFromAttr(attrs, "mrlRippleDuration", DEFAULT_DURATION);
        rippleAlphaInt = AttrUtils.getFloatFromAttr(attrs, "mrlRippleAlpha", DEFAULT_ALPHA);
        rippleDelayClick = AttrUtils.getBooleanFromAttr(attrs, "mrlRippleDelayClick", DEFAULT_DELAY_CLICK);
        rippleFadeDuration = AttrUtils.getIntegerFromAttr(attrs, "mrlRippleFadeDuration", DEFAULT_FADE_DURATION);
        ripplePersistent = AttrUtils.getBooleanFromAttr(attrs, "mrlRipplePersistent", DEFAULT_PERSISTENT);
        rippleInAdapter = AttrUtils.getBooleanFromAttr(attrs, "mrlRippleInAdapter", DEFAULT_SEARCH_ADAPTER);
        rippleRoundedCornersPx = AttrHelper.vp2px(AttrUtils.getDimensionFromAttr(attrs, "mrlRippleRoundedCorners",
                DEFAULT_ROUNDED_CORNERS_DP), context);
        paint.setAntiAlias(true);
        paint.setColor(new Color(rippleColor));
        paint.setAlpha(rippleAlphaInt);

        impl = new ViewGroupHelper(this);
        setTouchEventListener(this);
        addDrawTask(this);
        setLayoutRefreshedListener(component -> bounds.set(0, 0, getRight(), getBottom()));
    }

    private float getRadius() {
        return radius;
    }


    public void setRadius(float radius) {
        this.radius = radius;
        invalidate();
    }

    private float getEndRadius() {
        final int width = getWidth();
        final int height = getHeight();

        final int halfWidth = width / 2;
        final int halfHeight = height / 2;

        final float radiusX = halfWidth > currentCoordinates.getPointXToInt() ? width - currentCoordinates.getPointXToInt() : currentCoordinates.getPointXToInt();
        final float radiusY = halfHeight > currentCoordinates.getPointYToInt() ? height - currentCoordinates.getPointYToInt() : currentCoordinates.getPointYToInt();

        //noinspection
        return (float) Math.sqrt(Math.pow(radiusX, 2) + Math.pow(radiusY, 2)) * 1.2f;
    }

    public float getRippleAlphaInt() {
        return paint.getAlpha();
    }

    public void setRippleAlphaInt(float alphaInt) {
        paint.setAlpha(alphaInt);
        invalidate();
    }

    /**
     * Accessor
     *
     * @param rippleColor 颜色
     */
    public void setRippleColor(int rippleColor) {
        this.rippleColor = rippleColor;
        RgbColor rgbColor = RgbColor.fromArgbInt(rippleColor);
        this.rippleHighlightColor = Color.argb((int) (rgbColor.getAlpha() * 0.25), rgbColor.getRed(), rgbColor.getGreen(),
                rgbColor.getBlue());
        paint.setColor(new Color(rippleColor));
        paint.setAlpha(rippleAlphaInt);
        invalidate();
    }

    /**
     * setRippleHighlightColor
     *
     * @param rippleHighlightColor 背景色
     */
    public void setRippleHighlightColor(int rippleHighlightColor) {
        this.rippleHighlightColor = rippleHighlightColor;
        invalidate();
    }

    public void setRippleOverlay(boolean rippleOverlay) {
        this.rippleOverlay = rippleOverlay;
    }

    public void setRippleDiameterPx(int rippleDiameterPx) {
        this.rippleDiameterPx = rippleDiameterPx;
    }

    public void setRippleDuration(int rippleDuration) {
        this.rippleDuration = rippleDuration;
    }

    public void setRippleDelayClick(boolean rippleDelayClick) {
        this.rippleDelayClick = rippleDelayClick;
    }

    public void setRippleFadeDuration(int rippleFadeDuration) {
        this.rippleFadeDuration = rippleFadeDuration;
    }

    public void setRipplePersistent(boolean ripplePersistent) {
        this.ripplePersistent = ripplePersistent;
    }

    public void setRippleInAdapter(boolean rippleInAdapter) {
        this.rippleInAdapter = rippleInAdapter;
    }

    public void setRippleRoundedCornersPx(int rippleRoundedCornerPx) {
        this.rippleRoundedCornersPx = rippleRoundedCornerPx;
    }

    /**
     * setDefaultRippleAlphaInt
     *
     * @param alphaInt 透明度
     */
    public void setDefaultRippleAlphaInt(float alphaInt) {
        this.rippleAlphaInt = alphaInt;
        paint.setAlpha(alphaInt);
        invalidate();
    }

    /**
     * performRipple
     */
    public void performRipple() {
        currentCoordinates = new Point(getWidth() / 2, getHeight() / 2);
        startRipple(null);
    }

    /**
     * performRipple
     *
     * @param anchor anchor
     */
    public void performRipple(Point anchor) {
        currentCoordinates = new Point(anchor.getPointX(), anchor.getPointY());
        startRipple(null);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
        return DispatchHelper.dispatch(component, touchEvent);
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        impl.requestDisallowInterceptTouchEvent(disallowIntercept);
    }

    @Override
    public boolean onInterceptTouchEvent(TouchEvent event) {
        MmiPoint mmiPoint = event.getPointerScreenPosition(event.getIndex());
        return !findClickableViewInChild(childView, (int) mmiPoint.getX(), (int) mmiPoint.getY());
    }

    @Override
    public boolean dispatchTouchEvent(TouchEvent event) {
        return impl.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(TouchEvent event) {
        if (!isEnabled() || !childView.isEnabled()) {
            return false;
        }
        int pointerId = event.getPointerId(event.getIndex());
        MmiPoint mmiPoint = event.getPointerScreenPosition(pointerId);
        boolean isEventInBounds = includeScreen(mmiPoint);
        if (isEventInBounds) {
            MmiPoint pointerPosition = event.getPointerPosition(pointerId);
            previousCoordinates.modify(currentCoordinates.getPointX(), currentCoordinates.getPointY());
            float y = getY(mmiPoint);
            currentCoordinates.modify(pointerPosition.getX(), y);
        }

        boolean gestureResult = gestureDetector.onTouchEvent(event);
        if (!gestureResult && !mHasPerformedLongPress) {
            ShapeElement shapeElement = new ShapeElement();
            int action = event.getAction();
            switch (action) {
                case TouchEvent.PRIMARY_POINT_UP:
                    actionUp(isEventInBounds);
                    break;
                case TouchEvent.PRIMARY_POINT_DOWN:
                    actionDown(shapeElement);
                    break;
                case TouchEvent.CANCEL:
                    actionCancel();
                    break;
                case TouchEvent.POINT_MOVE:
                    actionMove(shapeElement, isEventInBounds);
                    break;
            }
        }
        return true;
    }

    private void actionMove(ShapeElement shapeElement, boolean isEventInBounds) {
        shapeElement.setRgbColor(RgbColor.fromArgbInt(rippleHighlightColor));
        if (isEventInBounds && !eventCancelled) {
            invalidate();
        } else if (!isEventInBounds) {
            startRipple(null);
        }

        if (!isEventInBounds) {
            cancelPressedEvent();
            if (hoverAnimator != null) {
                hoverAnimator.cancel();
            }
            eventCancelled = true;
        }
    }

    private void actionCancel() {
        if (rippleInAdapter) {
            // Do not use current coordinates in adapter since they tend to jump drastically on scroll.
            currentCoordinates.modify(previousCoordinates.getPointX(), previousCoordinates.getPointY());
            previousCoordinates = new Point();
        }
        startRipple(null);
        cancelPressedEvent();
    }

    private void actionDown(ShapeElement shapeElement) {
        shapeElement.setRgbColor(RgbColor.fromArgbInt(rippleHighlightColor));
        setBackground(shapeElement);

        setPositionInAdapter();
        eventCancelled = false;
        pendingPressEvent = new PressedEvent();
        if (isInScrollingContainer()) {
            cancelPressedEvent();
            prepressed = true;
            handler.postTask(pendingPressEvent, 100);
        } else {
            pendingPressEvent.run();
        }
    }

    private void actionUp(boolean isEventInBounds) {
        if (prepressed) {
            childView.setPressState(true);
            handler.postTask(new Runnable() {
                @Override
                public void run() {
                    childView.setPressState(false);
                }
            }, 64);
        }

        if (isEventInBounds) {
            PerformClickEvent pendingClickEvent = new PerformClickEvent();
            startRipple(pendingClickEvent);
            if (!rippleDelayClick) {
                pendingClickEvent.run();
            }
        }

        cancelPressedEvent();
    }

    private boolean includeScreen(MmiPoint mmiPoint) {
        int[] locationOnScreen = getLocationOnScreen();
        int x = (int) mmiPoint.getX();
        int y = (int) mmiPoint.getY();
        x -= locationOnScreen[0];
        y -= locationOnScreen[1];
        Rect rect = new Rect();
        getSelfVisibleRect(rect);
        return rect.isInclude(x, y);

    }

    private float getY(MmiPoint mmiPoint) {
        int[] locationOnScreen = getLocationOnScreen();
        float y = (int) mmiPoint.getY();
        y -= locationOnScreen[1];
        return y;
    }

    @Override
    public boolean isConsumed() {
        return impl.isConsumed();
    }


    /**
     * Helper
     */
    private class PerformClickEvent implements Runnable {

        @Override
        public void run() {
            if (mHasPerformedLongPress) {
                return;
            }

            if (getComponentParent() instanceof ListContainer) {
                clickAdapterView((ListContainer) getComponentParent());
            } else if (rippleInAdapter) {
                // Find adapter view.
                clickAdapterView(findParentAdapterView());
            } else {
                // Otherwise, just perform click on child.
                childView.simulateClick();
            }
        }

        private void clickAdapterView(ListContainer parent) {
            final int position = parent.getChildIndex(MaterialRippleLayout.this);
            if (position != -1) {
                parent.getComponentAt(position).simulateClick();
            }
        }
    }

    private final class PressedEvent implements Runnable {

        @Override
        public void run() {
            prepressed = false;
            // Prevent the child's long click, let the ripple layout call its performLongClick
            childView.setLongClickable(false);
            childView.setPressState(true);
            startHover();
        }
    }

    /**
     * Builder
     */
    public static class RippleBuilder {

        private final Context context;
        private final Component child;

        private int rippleColor = DEFAULT_COLOR;
        private int rippleHighlightColor;
        private boolean rippleOverlay = DEFAULT_RIPPLE_OVERLAY;
        private float rippleDiameterDp = DEFAULT_DIAMETER_DP;
        private int rippleDuration = DEFAULT_DURATION;
        private float rippleAlphaFloat = DEFAULT_ALPHA;
        private boolean rippleDelayClick = DEFAULT_DELAY_CLICK;
        private int rippleFadeDuration = DEFAULT_FADE_DURATION;
        private boolean ripplePersistent = DEFAULT_PERSISTENT;
        private boolean rippleSearchAdapter = DEFAULT_SEARCH_ADAPTER;
        private float rippleRoundedCornerDp = DEFAULT_ROUNDED_CORNERS_DP;

        public RippleBuilder(Component child) {
            this.child = child;
            this.context = child.getContext();
        }

        public RippleBuilder rippleColor(int color) {
            this.rippleColor = color;
            RgbColor rgbColor = RgbColor.fromArgbInt(rippleColor);
            this.rippleHighlightColor = Color.argb((int) (rgbColor.getAlpha() * 0.25), rgbColor.getRed(), rgbColor.getGreen(),
                    rgbColor.getBlue());
            return this;
        }

        /**
         * Make sure to call this after rippleColor() in the Builder.
         *
         * @param highlightColor highlightColor
         * @return RippleBuilder
         */
        public RippleBuilder rippleHighlightColor(int highlightColor) {
            this.rippleHighlightColor = highlightColor;
            return this;
        }

        /**
         * rippleOverlay
         *
         * @param overlay overlay
         * @return RippleBuilder
         */
        public RippleBuilder rippleOverlay(boolean overlay) {
            this.rippleOverlay = overlay;
            return this;
        }

        /**
         * rippleDiameterDp
         *
         * @param diameterDp diameterDp
         * @return RippleBuilder
         */
        public RippleBuilder rippleDiameterDp(float diameterDp) {
            this.rippleDiameterDp = diameterDp;
            return this;
        }

        /**
         * rippleDuration
         *
         * @param duration duration
         * @return RippleBuilder
         */
        public RippleBuilder rippleDuration(int duration) {
            this.rippleDuration = duration;
            return this;
        }

        /**
         * rippleAlpha
         *
         * @param alpha value between 0 and 1
         * @return RippleBuilder
         */
        public RippleBuilder rippleAlpha(float alpha) {
            this.rippleAlphaFloat = alpha;
            return this;
        }

        /**
         * rippleDelayClick
         *
         * @param delayClick delayClick
         * @return RippleBuilder
         */
        public RippleBuilder rippleDelayClick(boolean delayClick) {
            this.rippleDelayClick = delayClick;
            return this;
        }

        /**
         * rippleFadeDuration
         *
         * @param fadeDuration fadeDuration
         * @return RippleBuilder
         */
        public RippleBuilder rippleFadeDuration(int fadeDuration) {
            this.rippleFadeDuration = fadeDuration;
            return this;
        }

        /**
         * ripplePersistent
         *
         * @param persistent persistent
         * @return RippleBuilder
         */
        public RippleBuilder ripplePersistent(boolean persistent) {
            this.ripplePersistent = persistent;
            return this;
        }

        /**
         * rippleInAdapter
         *
         * @param inAdapter inAdapter
         * @return RippleBuilder
         */
        public RippleBuilder rippleInAdapter(boolean inAdapter) {
            this.rippleSearchAdapter = inAdapter;
            return this;
        }

        /**
         * rippleRoundedCornersDp
         *
         * @param radiusDp radiusDp
         * @return RippleBuilder
         */
        public RippleBuilder rippleRoundedCornersDp(float radiusDp) {
            this.rippleRoundedCornerDp = radiusDp;
            return this;
        }

        /**
         * create
         *
         * @return MaterialRippleLayout
         * @throws IllegalStateException IllegalStateException
         */
        public MaterialRippleLayout create() {
            MaterialRippleLayout layout = new MaterialRippleLayout(context);
            layout.setRippleColor(rippleColor);
            layout.setDefaultRippleAlphaInt(rippleAlphaFloat);
            layout.setRippleDelayClick(rippleDelayClick);
            layout.setRippleDiameterPx(AttrHelper.vp2px(rippleDiameterDp, context));
            layout.setRippleDuration(rippleDuration);
            layout.setRippleFadeDuration(rippleFadeDuration);
            layout.setRippleHighlightColor(rippleHighlightColor);
            layout.setRipplePersistent(ripplePersistent);
            layout.setRippleOverlay(rippleOverlay);
            layout.setRippleInAdapter(rippleSearchAdapter);
            layout.setRippleRoundedCornersPx(AttrHelper.vp2px(rippleRoundedCornerDp, context));
            ComponentContainer.LayoutConfig layoutConfig = child.getLayoutConfig();
            ComponentParent parent = child.getComponentParent();
            int index = 0;

            if (parent instanceof MaterialRippleLayout) {
                throw new IllegalStateException("MaterialRippleLayout could not be created: parent of the view already is a MaterialRippleLayout");
            }

            if (parent != null) {
                index = parent.getChildIndex(child);
                parent.removeComponent(child);
            }
            LayoutConfig config = new LayoutConfig(StackLayout.LayoutConfig.MATCH_CONTENT, StackLayout.LayoutConfig.MATCH_PARENT);
            config.alignment = LayoutAlignment.CENTER;
            layout.addComponent(child, config);

            if (parent instanceof ComponentContainer) {
                ComponentContainer container = (ComponentContainer) parent;
                container.addComponent(layout, index, layoutConfig);
            }
            return layout;
        }
    }
}
